import logging
import random
import traceback
import urllib.request
import time
import json
import baostock as bs
from flask import jsonify
from dao.db_connect import db_connect as db
import controller.carbon_finance.carbon_finance_utils as carbon_finance_utils

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0',
}

diagnosis_headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0',
    'X-Arsenal-Auth': 'mb_advance'
}
diagnosis_prefix = r'https://vaserviece.10jqka.com.cn/index/urp/getdata/basic?' \
                   r'tag=%E6%89%8B%E7%82%92%E5%88%86%E6%97%B6%E8%AF%8A%E8%82%A1_%E5%9F%BA%E5%87%86%E7%89%88%E6%9C%AC&codes='

success = 200  # 请求成功
# errors
identification_error = 100  # 未识别成功
internal_error = 500  # 服务器错误
data_missing = 101  # 数据库内无相关数据
usr_rank_error = 105  # 用户权限不足

# sina_shares = {
#     'name': 0,
#     'opening_price': 1,
#     'closing_price': 2,
#     'current_price': 3,
#     'max_price': 4,
#     'min_price': 5,
#     'trading': 8,
#     'business': 9,
#     'date': 30,
#     'time': 31,
# }

default_edge_num_conf = {
    'main_product': 5,
    'up_stream': 2,
    'down_stream': 2,
    'up_product': 2,
    'down_product': 2,
    'max_enterprise': 2,
}

SHARER_CACHE_EXPIRE_TIME = 3
CATEGORY_CACHE_EXPIRE_TIME = 120
DIAGNOSIS_CACHE_EXPIRE_TIME = 3600
RANGE_CACHE_EXPIRE_TIME = 3600
FINANCE_CACHE_EXPIRE_TIME = 3600 * 24
COMMON_CACHE_EXPIRE_TIME = 3600
HOLDER_CACHE_EXPIRE_TIME = 3600 * 24
PER_CACHE_EXPIRE_TIME = 3600 * 24

# shares_cache = {
#     'data': [],
#     'time': int(time.time())
# }

shares_cache = {}
shares_id_cache = []
shares_diagnosis_cache = {}
shares_range_cache = {}
shares_finance_cache = {}
shares_common_cache = {}
shares_holder_cache = {}
shares_per_cache = {}


class SharesHandler(object):
    def __init__(self):
        query_sql = f'select id, label, name, summary from carbon_shares order by id'
        data = db.queryAll(query_sql)
        shares_id_cache.clear()
        for share_data in data:
            share_id = share_data['id']
            shares_id_cache.append(str(share_id))

    @classmethod
    def get_diagnosis(cls, request):
        """
        获取诊股信息
        label: 获取的信息的标签
        id：股票的编号
        """
        msg = "获取信息成功"
        code = success
        logging.debug("working on get_diagnosis")
        label = request.values.get('label')
        share_id = request.values.get('id')

        # 参数异常处理
        if share_id is None or len(share_id) == 0 or label is None or len(label) == 0:
            msg = "参数异常，请检查参数！"
            code = identification_error
            return jsonify(code=code, data={}, msg=msg)

        if share_id not in shares_id_cache:
            msg = "id代表的股票不存在，请检查参数！"
            code = identification_error
            return jsonify(code=code, data={}, msg=msg)

        # 数据不存在 or 数据过时
        share_diagnosis = shares_diagnosis_cache.get(share_id)
        current_time = int(time.time())

        if share_diagnosis is None or share_diagnosis["init"] is False \
                or current_time - share_diagnosis["time"] > DIAGNOSIS_CACHE_EXPIRE_TIME:
            request = diagnosis_prefix + share_id
            req = urllib.request.Request(url=request, headers=diagnosis_headers)
            response = urllib.request.urlopen(req)

            # 读取并解码内容
            response_json = json.loads(response.read().decode("utf-8"))
            components = response_json["answer"]["components"]
            share_data = carbon_finance_utils.remake_components(components)

            share_diagnosis = {"init": True, "time": current_time, "data": share_data}
            shares_diagnosis_cache[share_id] = share_diagnosis

        data = share_diagnosis["data"].get(label)
        if data is None:
            msg = "label代表的信息不存在，请检查参数！"
            code = identification_error
            data = {}
        return jsonify(code=code, data=data, msg=msg)

    @classmethod
    def get_categories(cls, request):
        """
        获取股票门类
        """
        logging.debug("working on get_categories")
        msg = "获取列表成功"
        code = success
        try:
            query_sql = f'select category_id, category_name from carbon_shares_categories where enabled = 1'
            data = db.queryAll(query_sql)
            for category in data:
                category_id = category['category_id']
                current_time = int(time.time())
                shares_data = shares_cache.get(category_id)
                # 如果超过3秒，则重新计算，否则使用上次缓存的数据
                # 使用随机状态减少一次更新的数目
                random_expire_time = int(CATEGORY_CACHE_EXPIRE_TIME * random.random())
                if not shares_data or current_time - shares_data['time'] > random_expire_time:
                    if shares_data:
                        logging.debug("current_time - last_time:" + str(current_time - shares_data['time']))
                    logging.debug(
                        "random_expire_time:" + str(random_expire_time) + ", name:" + category['category_name'])
                    find_shares_sql = f'select share_id, share_label from categories_of_shares ' \
                                      f'where category_id = "{category_id}" order by share_id'
                    shares = db.queryAll(find_shares_sql)
                    if not shares or len(shares) == 0:
                        logging.debug("请求的种类不存在，请更换种类！" + category['category_name'])
                        continue
                    shares_data = carbon_finance_utils.update_shares_cache(shares)
                    if shares_data is None:
                        continue
                    shares_cache[category_id] = {
                        'data': shares_data,
                        'time': current_time,
                    }
                else:
                    shares_data = shares_data['data']
                category['share_list'] = shares_data[0:3]
        except Exception as e:
            msg = "获取列表失败"
            code = data_missing
            data = []
            logging.debug(traceback.format_exc())

        resp = {
            "code": code,
            "msg": msg,
            "data": data,
        }
        return jsonify(resp)

    @classmethod
    def get_shares(cls, request):
        """
        获取股票信息列表
        order: 排序列的名称
        is_asc: 否升序，为True是升序，为False是降序
        row_num: 每页行数
        page_num: 页号，从1开始
        """
        logging.debug("working on get_shares")
        msg = "获取列表成功"
        code = success
        page_data = []
        data = []
        category_id = request.values.get('category_id')

        if not category_id or len(category_id) == 0:
            category_id = "00000000000000000000000000000000"

        try:
            find_shares_sql = f'select share_id, share_label from categories_of_shares ' \
                              f'where category_id = "{category_id}" order by share_id'
            shares = db.queryAll(find_shares_sql)
            if not shares or len(shares) == 0:
                msg = "请求的种类不存在，请更换种类！"
                code = identification_error
                return jsonify(code=code, data={}, msg=msg)

            current_time = int(time.time())
            data = shares_cache.get(category_id)
            # 如果超过3秒，则重新计算，否则使用上次缓存的数据
            if not data or current_time - data['time'] > SHARER_CACHE_EXPIRE_TIME:
                data = carbon_finance_utils.update_shares_cache(shares)
                if data is None:
                    msg = "特殊异常！"
                    code = identification_error
                    return jsonify(code=code, data={}, msg=msg)

                shares_cache[category_id] = {
                    'data': data,
                    'time': current_time,
                }
            else:
                data = data['data']

            # 排序 & 分页
            order = request.values.get('order')
            if order is None or len(order) == 0:
                order = 'price_range'
            is_asc = request.values.get('is_asc')
            if is_asc is None or len(is_asc) == 0:
                is_asc = False
            else:
                if is_asc == 'True':
                    is_asc = True
                else:
                    is_asc = False
            row_num = request.values.get('row_num')
            if row_num is None or len(row_num) == 0:
                row_num = 20
            else:
                row_num = int(row_num)
            page_num = request.values.get('page_num')
            if page_num is None or len(page_num) == 0:
                page_num = 1
            else:
                page_num = int(page_num)

            def get_col(dic_elem):
                elem = dic_elem[order]
                if order == 'current_price' or order == 'price_range':
                    return float(elem)
                if order == 'id':
                    return int(elem)
                return elem

            data.sort(key=get_col, reverse=not is_asc)
            page_data = data[(page_num - 1) * row_num: page_num * row_num]

        except Exception as e:
            msg = "获取列表失败"
            code = data_missing
            logging.debug(traceback.format_exc())

        resp = {
            "code": code,
            "msg": msg,
            "data": page_data,
            "total": len(data)
        }
        return jsonify(resp)

    @classmethod
    def get_daily_image(cls, request):
        """
        获取每日的K线图链接，返回url
        id：股票的编号
        label: 获取的信息的标签
        """
        logging.debug("working on get_daily_image")
        msg = "获取链接成功"
        code = success
        id = request.values.get('id')
        label = str(request.values.get('label')).lower()
        url = f'http://image.sinajs.cn/newchart/daily/n/{label}{id}.gif'
        if id is None or len(id) == 0 or label is None or len(label) == 0:
            msg = "参数异常，请检查参数！"
            code = identification_error
            url = ''

        resp = {
            "code": code,
            "msg": msg,
            "data": url
        }
        return jsonify(resp)

    @classmethod
    def get_range_image(cls, request):
        """
        获取累计涨跌幅数据
        """
        logging.debug("working on get_range_image")
        msg = "获取数据成功"
        code = success

        share_range = shares_range_cache.get("data")
        current_time = int(time.time())

        if share_range is None or current_time - shares_range_cache["time"] > RANGE_CACHE_EXPIRE_TIME:
            url = f'http://www.iwencai.com/unifiedwap/unified-wap/result/concept-detail?concept_id=308728'
            req = urllib.request.Request(url=url, headers=headers)

            while_count = 0
            while True:
                while_count = while_count + 1
                response = urllib.request.urlopen(req)
                # 读取并解码内容
                try:
                    response_json = json.loads(response.read().decode("utf-8"))
                    response_dic = response_json.get('data').get('data')
                    if response_dic is not None:
                        break
                except Exception as e:
                    logging.debug(traceback.format_exc())
                if while_count == 10:
                    msg = "接口异常！"
                    code = identification_error
                    return jsonify(code=code, data={}, msg=msg)
            date_array = []
            hq_array = []
            carbon_array = []
            hq_300_datas = response_dic["hq300_data"]
            carbon_datas = response_dic["concept_data"]
            for date in hq_300_datas:
                date_array.append(date)
                hq_array.append(float(hq_300_datas[date]))
                carbon_array.append(float(carbon_datas[date]))
            share_range = {
                "dateArray": date_array,
                "hqArray": hq_array,
                "carbonArray": carbon_array,
            }
            shares_range_cache['data'] = share_range
            shares_range_cache['time'] = current_time

        resp = {
            "code": code,
            "msg": msg,
            "data": share_range
        }
        return jsonify(resp)

    @classmethod
    def get_base_info(cls, request):
        """
        获取股票基本概况
        id: 股票的编号
        """
        logging.debug("working on get_base_info")
        msg = "获取列表成功"
        code = success
        data = {}
        share_id = request.values.get('id')
        if share_id is None or len(share_id) == 0:
            return jsonify(code=identification_error, data={}, msg="缺少参数！")
        try:
            query_sql = f'select id, name, city, industry, products, concept from carbon_shares where id={share_id}'
            data = db.queryOne(query_sql)
        except Exception as e:
            msg = "获取列表失败"
            code = data_missing
            logging.debug(traceback.format_exc())

        resp = {
            "code": code,
            "msg": msg,
            "data": [data],
        }
        return jsonify(resp)

    @classmethod
    def get_finance_year(cls, request):
        """
        获取股票最近一年的股票财务数据
        id：股票的编号
        label: 获取的信息的标签
        """
        logging.debug("working on get_finance_year")
        # 获取股票代码
        code = success
        msg = "获取成功！"
        share_id = request.values.get('id')
        label = str(request.values.get('label')).lower()
        if share_id is None or len(share_id) == 0 or label is None or len(label) == 0:
            msg = "参数异常，请检查参数！"
            code = identification_error
            return jsonify(code=code, data=[], msg=msg)
        share_code = label + '.' + share_id

        if share_id not in shares_id_cache:
            msg = "id代表的股票不存在，请检查参数！"
            code = identification_error
            return jsonify(code=code, data={}, msg=msg)

        # 数据不存在 or 数据过时
        share_finance = shares_finance_cache.get(share_id)
        current_time = int(time.time())

        if share_finance is None or share_finance["init"] is False \
                or current_time - share_finance["time"] > FINANCE_CACHE_EXPIRE_TIME:
            # 生成财报列表
            year = time.localtime().tm_year
            month = time.localtime().tm_mon
            begin_season = int((month - 1) / 3)
            share_year_list = []
            for index in range(4):
                if begin_season == 0:
                    year = year - 1
                    begin_season = 4
                share_year_list.append((year, begin_season))
                begin_season = begin_season - 1

            code, msg, data = carbon_finance_utils.finance_generate(share_year_list, share_code)

            # 如果财报还没生成，修改日期重新查询
            if code == identification_error:
                begin_season = int((month - 1) / 3 + 3) % 4
                share_year_list = []
                for index in range(4):
                    if begin_season == 0:
                        year = year - 1
                        begin_season = 4
                    share_year_list.append((year, begin_season))
                    begin_season = begin_season - 1
                code, msg, data = carbon_finance_utils.finance_generate(share_year_list, share_code)

            if code != success:
                data = []
            else:
                share_finance = {
                    "data": data,
                    "init": True,
                    "time": current_time
                }
                shares_finance_cache[share_id] = share_finance
        else:
            data = share_finance['data']
        return jsonify(code=code, data=data, msg=msg)

    @classmethod
    def get_common(cls, request):
        """
        获取股票当前的一些指标
        id：股票的编号
        label: 获取的信息的标签
        """
        logging.debug("working on get_common")
        # 获取股票代码
        code = success
        msg = "获取成功！"
        share_id = request.values.get('id')
        label = str(request.values.get('label')).lower()
        if share_id is None or len(share_id) == 0 or label is None or len(label) == 0:
            msg = "参数异常，请检查参数！"
            code = identification_error
            return jsonify(code=code, data=[], msg=msg)

        if share_id not in shares_id_cache:
            msg = "id代表的股票不存在，请检查参数！"
            code = identification_error
            return jsonify(code=code, data={}, msg=msg)

        # 数据不存在 or 数据过时
        share_common = shares_common_cache.get(share_id)
        current_time = int(time.time())

        if share_common is None or share_common["init"] is False \
                or current_time - share_common["time"] > COMMON_CACHE_EXPIRE_TIME:

            # 生成数据
            year = time.localtime().tm_year
            month = time.localtime().tm_mon
            date = time.strftime('%Y-%m-%d', time.localtime(time.time() - 3600 * 24))
            begin_season = int((month - 1) / 3)
            finance_time = (year, begin_season, date)
            code, msg, data = carbon_finance_utils.common_generate(finance_time, label, share_id)

            # 如果财报还没生成，修改日期重新查询
            if code == identification_error:
                begin_season = int((month - 1) / 3 + 3) % 4
                finance_time = (year, begin_season, date)
                code, msg, data = carbon_finance_utils.common_generate(finance_time, label, share_id)

            if code != success:
                data = None
            else:
                share_common = {
                    "data": data,
                    "init": True,
                    "time": current_time
                }
                shares_common_cache[share_id] = share_common
        else:
            data = share_common['data']
        return jsonify(code=code, data=[data], msg=msg)

    @classmethod
    def get_knowledge_graph(cls, request):
        """
        获取股票的产品知识图谱
        id：股票的编号
        """
        logging.debug("working on get_knowledge_graph")
        # 获取股票代码
        code = success
        msg = "获取成功！"
        share_id = request.values.get('id')
        if share_id is None or len(share_id) == 0:
            msg = "参数异常，请检查参数！"
            code = identification_error
            return jsonify(code=code, data=[], msg=msg)

        if share_id not in shares_id_cache:
            msg = "id代表的股票不存在，请检查参数！"
            code = identification_error
            return jsonify(code=code, data={}, msg=msg)

        """
        查询知识图谱数据流程：
            1. 使用股票 id 找到对应的企业节点
            2. 查找 企业节点 的 主营产品
            3. 找到该 主营产品 的 上游、下游、下位产品 以及 上位产品
            4. 找到与以上这些产品相关的 企业节点
            5. 找到所有产品节点
            default_edge_num_conf = {
                'main_product': 5,
                'up_stream': 2,
                'down_stream': 2,
                'up_product': 2,
                'down_product': 2,
                'max_enterprise': 2,
            }
        """
        max_main_product = request.values.get('max_product')
        if max_main_product is None or len(max_main_product) == 0:
            max_main_product = default_edge_num_conf['main_product']
        else:
            max_main_product = int(max_main_product)

        max_up_stream = request.values.get('up_stream')
        if max_up_stream is None or len(max_up_stream) == 0:
            max_up_stream = default_edge_num_conf['up_stream']
        else:
            max_up_stream = int(max_up_stream)

        max_down_stream = request.values.get('down_stream')
        if max_down_stream is None or len(max_down_stream) == 0:
            max_down_stream = default_edge_num_conf['down_stream']
        else:
            max_down_stream = int(max_down_stream)

        max_up_product = request.values.get('up_product')
        if max_up_product is None or len(max_up_product) == 0:
            max_up_product = default_edge_num_conf['up_product']
        else:
            max_up_product = int(max_up_product)

        max_down_product = request.values.get('down_product')
        if max_down_product is None or len(max_down_product) == 0:
            max_down_product = default_edge_num_conf['down_product']
        else:
            max_down_product = int(max_down_product)

        max_enterprise = request.values.get('max_enterprise')
        if max_enterprise is None or len(max_enterprise) == 0:
            max_enterprise = default_edge_num_conf['max_enterprise']
        else:
            max_enterprise = int(max_enterprise)

        data = {}
        nodes = []
        edges = []
        try:
            # 使用股票 id 找到对应的企业节点
            query_find_id = f'select id, code, name from enterprises where code = "{share_id}"'
            query_data = db.queryOne(query_find_id)

            # print(query_data)
            if query_data is None:
                msg = "当前股票不存在产品图谱！"
                code = data_missing
                return jsonify(code=code, data={}, msg=msg)
            main_id = query_data['id']
            main_node = {
                'id': main_id,
                'node_code': query_data['code'],
                'name': query_data['name'] + '\n' + query_data['code'],
                'node_domain': '企业',
                'group': 1,
            }
            nodes.append(main_node)

            # 查找 企业节点 的 主营产品
            query_find_main_product = f'select id, from_id, to_id, score from main_product_edge ' \
                                      f'where from_id = "{main_id}" ' \
                                      f'order by score desc limit {max_main_product}'
            main_product_edges = db.queryAll(query_find_main_product)

            # print(main_product_edges)
            main_product_ids = set()
            for main_product_edge in main_product_edges:
                main_product_edge['edge_name'] = '主营产品'
                edge = {
                    "id": main_product_edge['id'],
                    "source": main_product_edge['from_id'],
                    "target": main_product_edge['to_id'],
                    "value": main_product_edge['edge_name'],
                    "score": main_product_edge['score']
                }
                edges.append(edge)
                main_product_ids.add(main_product_edge['to_id'])

            # 找到该主营产品的上游、下游、下位产品以及上位产品
            query_find_stream = f'select id, from_id, to_id, score from up_down_stream_edge ' \
                                f'where from_id in {tuple(main_product_ids)} ' \
                                f'or to_id in {tuple(main_product_ids)} order by score desc'

            streams = db.queryAll(query_find_stream)
            query_find_product = f'select id, from_id, to_id, score from up_down_product_edge ' \
                                 f'where from_id in {tuple(main_product_ids)} ' \
                                 f'or to_id in {tuple(main_product_ids)} order by score desc'
            products = db.queryAll(query_find_product)
            product_ids = main_product_ids.copy()

            up_count = {}
            down_count = {}
            for stream in streams:
                from_id = stream['from_id']
                to_id = stream['to_id']
                if from_id in main_product_ids:
                    if down_count.get(from_id) is None:
                        down_count[from_id] = 0
                    if down_count[from_id] >= max_down_stream:
                        continue
                    down_count[from_id] = down_count[from_id] + 1
                    product_ids.add(stream['to_id'])
                    stream['edge_name'] = '下游'

                if from_id not in main_product_ids and to_id in main_product_ids:
                    if up_count.get(to_id) is None:
                        up_count[to_id] = 0
                    if up_count[to_id] >= max_up_stream:
                        continue
                    up_count[to_id] = up_count[to_id] + 1
                    product_ids.add(stream['from_id'])
                    stream['edge_name'] = '上游'

                edge = {
                    "id": stream['id'],
                    "source": stream['from_id'],
                    "target": stream['to_id'],
                    "value": stream['edge_name'],
                    "score": stream['score']
                }
                edges.append(edge)

            up_count.clear()
            down_count.clear()
            for product in products:
                from_id = product['from_id']
                to_id = product['to_id']
                if from_id in main_product_ids:
                    if down_count.get(from_id) is None:
                        down_count[from_id] = 0
                    if down_count[from_id] >= max_down_product:
                        continue
                    down_count[from_id] = down_count[from_id] + 1
                    product_ids.add(product['to_id'])
                    product['edge_name'] = '下位产品'

                if from_id not in main_product_ids and to_id in main_product_ids:
                    if up_count.get(to_id) is None:
                        up_count[to_id] = 0
                    if up_count[to_id] >= max_up_product:
                        continue
                    up_count[to_id] = up_count[to_id] + 1
                    product_ids.add(product['from_id'])
                    product['edge_name'] = '上位产品'

                edge = {
                    "id": product['id'],
                    "source": product['from_id'],
                    "target": product['to_id'],
                    "value": product['edge_name'],
                    "score": product['score']
                }
                edges.append(edge)

            # 找到与以上这些产品相关的 企业节点
            query_find_main_product = f'select id, from_id, to_id, score from main_product_edge ' \
                                      f'where to_id in {tuple(product_ids)} '
            main_product_edges = db.queryAll(query_find_main_product)

            # print(main_product_edges)
            enterprise_ids = set()
            enterprise_count = {}
            for main_product_edge in main_product_edges:
                to_id = main_product_edge['to_id']
                if enterprise_count.get(to_id) is None:
                    enterprise_count[to_id] = 0
                if enterprise_count[to_id] >= max_enterprise:
                    continue
                enterprise_count[to_id] = enterprise_count[to_id] + 1
                main_product_edge['edge_name'] = '主营产品'
                edge = {
                    "id": main_product_edge['id'],
                    "source": main_product_edge['from_id'],
                    "target": main_product_edge['to_id'],
                    "value": main_product_edge['edge_name'],
                    "score": main_product_edge['score']
                }
                edges.append(edge)
                enterprise_ids.add(main_product_edge['from_id'])

            query_find_id = f'select id, code, name from enterprises ' \
                            f'where id in {tuple(enterprise_ids)} and id != "{main_id}" '
            enterprise_nodes = db.queryAll(query_find_id)

            # print(enterprise_nodes)
            for enterprise_node in enterprise_nodes:
                main_node = {
                    'id': enterprise_node['id'],
                    'node_code': enterprise_node['code'],
                    'name': enterprise_node['name'] + '\n' + enterprise_node['code'],
                    'node_domain': '企业',
                    'group': 3,
                }
                nodes.append(main_node)

            # 找到以上这些产品节点
            query_find_product = f'select id, code, name from products where id in {tuple(product_ids)}'
            products = db.queryAll(query_find_product)

            # print(products)
            for product in products:
                product_node = {
                    'id': product['id'],
                    'node_code': product['code'],
                    'name': product['name'],
                    'node_domain': '产品',
                    'group': 2,
                }
                nodes.append(product_node)
            data = {
                "nodes": nodes,
                "edges": edges
            }
        except Exception as e:
            msg = "获取列表失败"
            code = data_missing
            logging.debug(traceback.format_exc())

        resp = {
            "code": code,
            "msg": msg,
            "data": data
        }
        return jsonify(resp)

    @classmethod
    def get_shareholder_change(cls, request):
        """
        获取股东变化情况
        id：股票的编号
        """
        logging.debug("working on get_shareholder_change")
        msg = "获取数据成功"
        code = success

        share_id = request.values.get('id')
        if share_id is None or len(share_id) == 0:
            msg = "参数异常，请检查参数！"
            code = identification_error
            return jsonify(code=code, data=[], msg=msg)

        if share_id not in shares_id_cache:
            msg = "id代表的股票不存在，请检查参数！"
            code = identification_error
            return jsonify(code=code, data={}, msg=msg)
        # 数据不存在 or 数据过时
        shares_holder = shares_holder_cache.get(share_id)
        current_time = int(time.time())
        if shares_holder is None or current_time - shares_holder["time"] > HOLDER_CACHE_EXPIRE_TIME:
            url = f'http://stock.jrj.com.cn/action/gudong/getGudongDataByCode.jspa?stockcode={share_id}'
            req = urllib.request.Request(url=url, headers=headers)

            while_count = 0
            while True:
                while_count = while_count + 1
                response = urllib.request.urlopen(req)
                # 读取并解码内容
                share_text = str(response.read().decode("utf-8"))
                if share_text is not None and len(share_text) != 0:
                    break
                if while_count == 10:
                    msg = "接口异常！"
                    code = identification_error
                    return jsonify(code=code, data={}, msg=msg)

            share_string = share_text[share_text.find('=') + 1: -1]
            share_string = share_string.replace('\'', '"').replace('enddate', '"enddate"').replace('data', '"data"')
            share_string = share_string.replace('new_num', '"new_num"').replace('avg_num', '"avg_num"')
            response_dic = json.loads(share_string)
            # print(response_dic)

            shares_holder = {
                "data": {
                    "dateArray": response_dic['enddate']['data'],
                    "holder_num": response_dic['new_num']['data'],
                    "holder_avg": response_dic['avg_num']['data'],
                },
                "time": current_time
            }
            shares_holder_cache[share_id] = shares_holder

        resp = {
            "code": code,
            "msg": msg,
            "data": shares_holder['data']
        }
        return jsonify(resp)

    @classmethod
    def get_price_earning_ratio(cls, request):
        """
        获取历史市盈率
        id：股票的编号
        label：股票上市的交易所标志
        """
        logging.debug("working on get_price_earning_ratio")
        msg = "获取数据成功"
        code = success

        share_id = request.values.get('id')
        share_label = str(request.values.get('label')).lower()
        if share_id is None or len(share_id) == 0 or share_label is None or len(share_label) == 0:
            msg = "参数异常，请检查参数！"
            code = identification_error
            return jsonify(code=code, data=[], msg=msg)

        if share_id not in shares_id_cache:
            msg = "id代表的股票不存在，请检查参数！"
            code = identification_error
            return jsonify(code=code, data={}, msg=msg)

        # 数据不存在 or 数据过时
        shares_per = shares_per_cache.get(share_id)
        current_time = int(time.time())
        if shares_per is None or current_time - shares_per["time"] > PER_CACHE_EXPIRE_TIME:
            url = f'https://eniu.com/chart/pea/{share_label}{share_id}/t/all'
            req = urllib.request.Request(url=url, headers=headers)

            while_count = 0
            while True:
                while_count = while_count + 1
                try:
                    response = urllib.request.urlopen(req)
                    # 读取并解码内容
                    response_dic = json.loads(response.read().decode("utf-8"))
                except Exception as e:
                    msg = "参数异常，请检查参数！"
                    code = identification_error
                    return jsonify(code=code, data={}, msg=msg)

                if response_dic is not None:
                    break
                if while_count == 10:
                    msg = "接口异常！"
                    code = identification_error
                    return jsonify(code=code, data={}, msg=msg)

            shares_per = {
                "data": response_dic,
                "time": current_time
            }
            shares_per_cache[share_id] = shares_per

        resp = {
            "code": code,
            "msg": msg,
            "data": shares_per['data']
        }
        return jsonify(resp)
